package com.ibm.android.camera;

import java.io.IOException;

import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;

public class CameraManager implements Callback {
	private static final String TAG = CameraManager.class.getSimpleName();

	private static final int MIN_FRAME_WIDTH = 240;
	private static final int MIN_FRAME_HEIGHT = 240;
	private static final int MAX_FRAME_WIDTH = 480;
	private static final int MAX_FRAME_HEIGHT = 360;

	private static CameraManager mCameraManager;
	private final CameraConfigurationManager mConfigManager;
	private ICamera mCamera;
	private Rect mFramingRect;

	private boolean mIsPreviewing;
	private boolean mStartPreviewing;
	private boolean mHasHolder;
	private boolean useOneShotPreviewCallback;
	private boolean mPreviewRequested;

	private PreviewCallback mPreviewCallback;

	public static void init(Context context, ICamera camera) {
		if (mCameraManager == null) {
			mCameraManager = new CameraManager(context, camera);
		}
	}

	public static CameraManager get() {
		return mCameraManager;
	}

	public Rect getFramingRect() {
		Point screenResolution = mConfigManager.getScreenResolution();
		if (mFramingRect == null) {
			if (mCamera == null) {
				return null;
			}
			int width = screenResolution.x * 3 / 4;
			if (width < MIN_FRAME_WIDTH) {
				width = MIN_FRAME_WIDTH;
			} else if (width > MAX_FRAME_WIDTH) {
				width = MAX_FRAME_WIDTH;
			}
			int height = screenResolution.y * 3 / 4;
			if (height < MIN_FRAME_HEIGHT) {
				height = MIN_FRAME_HEIGHT;
			} else if (height > MAX_FRAME_HEIGHT) {
				height = MAX_FRAME_HEIGHT;
			}
			int leftOffset = (screenResolution.x - width) / 2;
			int topOffset = (screenResolution.y - height) / 2;
			mFramingRect = new Rect(leftOffset, topOffset, leftOffset + width,
					topOffset + height);
			Log.d(TAG, "Calculated framing rect: " + mFramingRect);
		}
		return mFramingRect;
	}

	public void openDriver(SurfaceHolder holder) {
		holder.addCallback(this);
		mCamera.open();
	}

	public void closeDriver() {
		mCamera.stopPreview();
		mCamera.release();
		mIsPreviewing = false;
		mHasHolder = false;
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		try {
			mCamera.setPreviewDisplay(holder);
			mHasHolder = true;
			if (mStartPreviewing && !mIsPreviewing) {
				startPreview();
				if (mPreviewRequested)
					sendPreview();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		mCamera.stopPreview();
		mHasHolder = false;
	}

	public void startPreview() {
		mStartPreviewing = true;
		if (mHasHolder) {
			mCamera.startPreview();
			mIsPreviewing = true;
		}
	}
	
	public void stopPreview() {
		mStartPreviewing = false;
		if (mHasHolder) {
			mCamera.stopPreview();
		}
		mIsPreviewing = false;
	}

	/** Private methods */
	private CameraManager(Context context, ICamera camera) {
		mCamera = camera;
		mConfigManager = new CameraConfigurationManager(context, mCamera);

		// Camera.setOneShotPreviewCallback() has a race condition in Cupcake,
		// so we use the older Camera.setPreviewCallback() on 1.5 and earlier.
		// For Donut and later, we need to use the more efficient one shot
		// callback, as the older one can swamp the system and cause it to run
		// out of memory. We can't use SDK_INT because it was introduced in the
		// Donut SDK.
		//
		// useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) >
		// Build.VERSION_CODES.CUPCAKE;
		// 3 = Cupcake
		useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3;
		mPreviewCallback = new PreviewCallback(camera,
				useOneShotPreviewCallback);

		mIsPreviewing = false;
		mStartPreviewing = false;
		mHasHolder = false;
		mPreviewRequested = false;
	}

	public void requestPreview(Handler handler, int message) {
		mPreviewCallback.setHandler(handler, message);
		if (mIsPreviewing) {
			sendPreview();
		} else {
			mPreviewRequested = true;
		}
	}

	private void sendPreview() {
		mPreviewRequested = false;
		mCamera.setPreviewCallback(mPreviewCallback);

		if (useOneShotPreviewCallback) {
			mCamera.setOneShotPreviewCallback(mPreviewCallback);
		} else {
			mCamera.setPreviewCallback(mPreviewCallback);
		}
	}

	/**
	 * @see PixelFormat documenation.
	 */
	public int getPreviewFormat() {
		return mCamera.getParameters().getPreviewFormat();
	}
}
